"""
解析注解RMI
"""
import re
import os
import string


class Param:
    """
    存储方法的参数类型
    """
    PATTERN_VAR = re.compile(r'[a-zA-Z0-9_]+')

    def __init__(self, ctx):
        self.content = ctx
        self.type = ""
        self.name = ""
        self.base_type = ""
        self.is_const = False
        self.is_point = False
        self.is_ref = False
        # self.is_forward = False

    pass

    def parser(self):
        # print(self.context)
        self.is_const = self.content.find("const") > -1
        self.is_point = self.content.find("*") > -1
        self.is_ref = self.content.find("&") > -1
        # self.is_forward = self.string.find("&&") > 0
        split = len(self.content)
        while split > 0:
            split -= 1
            if self.content[split] == ' ' or self.content[split] == '*' or self.content[split] == '&':
                break
        self.type = self.content[0: split + 1].strip()
        self.name = self.content[split + 1:].strip()
        res = re.findall(Param.PATTERN_VAR, self.type)
        self.base_type = res[len(res) - 1]
        return len(self.type) > 0 and len(self.name) > 0

    pass

    def __str__(self):
        return '[type:' + self.type + ', name:' + self.name + ', const:' + str(self.is_const) + ', point:' + str(
            self.is_point) \
               + ', ref:' + str(self.is_ref) + ']'

    pass


class RMIFunc:
    """
    存储RMI的方法类型
    """

    def __init__(self, ctx):
        ctx = ctx.strip()
        self._func_str = ctx
        self.name = ""
        self.ret_type = ""
        self.params = []

    pass

    def _parser_return_and_func_name(self, ctx):
        """
        提取返回值和函数名称
        """
        split = len(ctx)
        while split > 0:
            split -= 1
            if ctx[split] == ' ' or ctx[split] == '*' or ctx[split] == '&':
                break
        self.name = ctx[split + 1:].strip()
        self.ret_type = ctx[0: split + 1].strip()

    pass

    def _parser_param(self, ctx):
        """
        提取参数
        """
        if len(ctx) == 0:
            return True

        params = ctx.split(',')
        for p in params:
            res = Param(p.strip())
            if not res.parser():
                return False
            # print(res)
            self.params.append(res)
        # print(str(self.params))
        return True

    pass

    def parse(self):
        param_start_index = self._func_str.find('(')
        param_start_end = self._func_str.find(')')
        # print("%d-%d" % (param_start_index, param_start_end))
        ret_func_name = self._func_str[:param_start_index]
        self._parser_return_and_func_name(ret_func_name.strip())
        # print("-->"+ret_func_name)
        func_param = self._func_str[param_start_index + 1:param_start_end]
        # print("-->" + func_param)
        return self._parser_param(func_param)

    pass


class ModuleRmi:
    """
    解析某个模块的所有rmi function
    """
    __single_line_commit_pattern = re.compile(r'//(.*)', re.M)
    __multi_line_commit_pattern = re.compile(r'/\*(.*?)\*/', re.S)
    __func_line_break_pattern = re.compile(r'([\n]+)|([ ]{2,})', re.S)

    def __init__(self, _context, _name):
        self.name = _name
        self.cls_ctx = _context
        self.rmi_func = []
        self.no_rmi = True

    pass

    def _clean_multi_line_comment(self):
        """
        去掉多行注释
        """
        self.cls_ctx = re.sub(self.__multi_line_commit_pattern, '', self.cls_ctx)

    pass

    def _clean_single_line_comment(self):
        """
        去掉C++的单行注释
        """
        self.cls_ctx = re.sub(self.__single_line_commit_pattern, '', self.cls_ctx)

    pass

    def _clean_empty_line(self):
        """
        去掉空行
        """
        lines = self.cls_ctx.split('\n')
        self.cls_ctx = ""
        for line in lines:
            line = line.strip()
            if line:
                self.cls_ctx += line + '\n'

    pass

    def _format_to_one_line(self):
        pattern = re.compile(r'(virtual (.+?);)|(Annotation\(@(.+?)\))', re.S)
        func = re.findall(pattern, self.cls_ctx)
        is_rmi_func = False
        for fun in func:
            if fun[0]:
                if not is_rmi_func:
                    continue
                # 只解析rmi function
                is_rmi_func = False
                line_clear = re.sub(self.__func_line_break_pattern, ' ', fun[1])
                # print(line_clear)
                rmi_function = RMIFunc(line_clear)
                self.no_rmi = False
                if rmi_function.parse():
                    self.rmi_func.append(rmi_function)
            else:
                is_rmi_func = fun[2].find("@RMI") > -1

    pass

    def module_full_name(self):
        return 'I' + self.name + "Manager"

    pass

    def parser(self):
        self._clean_single_line_comment()
        self._clean_multi_line_comment()
        self._clean_empty_line()
        self._format_to_one_line()
    pass

    def empty(self):
        return self.no_rmi
    pass


class FileParser:

    def __init__(self, file):
        self.file_path = file
        self.file_context = ""
        self.rmi_module = []

    pass

    def parser(self):
        if not os.path.isfile(self.file_path):
            print("is not file!")
            return
        with open(self.file_path, "rt", encoding="utf-8", errors="ignore") as f:
            self.file_context = f.read()
            pattern = re.compile(r'class I(.+?)Manager(.+?)\};', re.S)
            res = re.findall(pattern, self.file_context)
            for r in res:
                m = ModuleRmi(r[1], r[0])
                m.parser()
                if not m.empty():
                    self.rmi_module.append(m)
    pass

    def rmi_modules(self):
        return self.rmi_module


class RmiFile:
    """
    生成rmi file
    """
    TEMPLATE_RMI_SINGLE_FUNCTION_ID = string.Template('__${module_name}_RMI_FUNC_${func_name}__')
    TEMPLATE_RMI_STRUCT = string.Template("""
template <>
class CRmiStubWrapper<${mod_name}> : public IRmiStubImpl
{
public:
    explicit CRmiStubWrapper(${mod_name}* p)
        : m_p(p)
    {
        
    }
${rmi_call}
private:
${rmi_func_id}
${rmi_handle}
${rmi_handle_case}
${rmi_co_handle_case}
    void set_service_token(const service_token_t& tk) override
    {
        m_token.set_type(tk.type);
        m_token.set_id(tk.id);
    }
private:
    ${mod_name}* m_p;
    protobuf_service_token m_token;
};
""")

    TABLE = "    "
    TABLE2 = TABLE * 2
    TABLE3 = TABLE * 3
    TABLE4 = TABLE * 4
    TABLE5 = TABLE * 5
    TABLE6 = TABLE * 6

    LISENCE = """#pragma once
// License see: https://opensource.org/licenses/MIT
// 本文件由工具自动生成, 请勿修改

"""

    def __init__(self, _origin_file, _modules):
        self.origin_file = os.path.abspath(_origin_file)
        self.modules = _modules
    pass

    
    @staticmethod
    def _to_func_id(mod_name, func_name):
        return RmiFile.TEMPLATE_RMI_SINGLE_FUNCTION_ID.substitute(module_name=mod_name, func_name=func_name)
    pass

    @staticmethod
    def _to_function_enum(module):
        s = RmiFile.TABLE + 'enum RmiFuncId\n'
        s += RmiFile.TABLE + "{\n"
        for func in module.rmi_func:
            # print(module.name + ":" + func.name)
            s += RmiFile.TABLE2 + RmiFile._to_func_id(module.name, func.name) + ',\n'
        s += RmiFile.TABLE + "};\n\n"
        return s
    pass

    def _to_single_handle_func(module, func):
        """
        格式化出类似如下的
        void rc_test_call(const RemoteCallMessage& call)
        {
            const auto& params = call.params();
            auto a = ParseFromString<int>(params.Get(0));
            auto b = ParseFromString<int>(params.Get(1));
            m_p->rc_test(a, b);
        }
        """
        # 构造函数名
        s = RmiFile.TABLE + "void "
        s += func.name + "_handle(const RemoteCallMessage& call)\n"
        s += RmiFile.TABLE + "{\n"
        s += RmiFile.TABLE2 + "const auto& params = call.params();\n"
        ps = ""

        for i in range(len(func.params)):
            if i == 0:
                ps += "ParseFromString<{}>(params.Get({}))".format(func.params[i].base_type, i);
            else:
                ps += ", ParseFromString<{}>(params.Get({}))".format(func.params[i].base_type, i);
        
        s += RmiFile.TABLE2 + "m_p->" + func.name + "(" + ps + ");\n"
        s += RmiFile.TABLE + "}\n\n"
        return s
    pass

    def _to_single_handle_co_func(module, func):
        """
        格式化出类似如下的
        cppcoro::task<> rc_test_co_call(const RemoteCallMessage& call, std::string& back)
        {
            const auto& params = call.params();
            auto a = ParseFromString<int>(params.Get(0));
            auto b = ParseFromString<int>(params.Get(1));
            back = SerializeAsString(m_p->rc_test(a, b));
            co_return;
        }
        """
        s = ""
        # 构造函数名
        s += RmiFile.TABLE + "cppcoro::task<> " + func.name 
        s += "_co_handle(const RemoteCallMessage& call, std::string& back)\n"
        s += RmiFile.TABLE + "{\n"
        s += RmiFile.TABLE2 + "const auto& params = call.params();\n"
        ps = ""

        for i in range(len(func.params)):
            if i == 0:
                ps += "ParseFromString<{}>(params.Get({}))".format(func.params[i].base_type, i);
            else:
                ps += ", ParseFromString<{}>(params.Get({}))".format(func.params[i].base_type, i);
        if func.ret_type != "void":
            s += RmiFile.TABLE2 + "back = SerializeAsString(m_p->" + func.name + "(" + ps + "));\n"
        else:
            s += RmiFile.TABLE2 + "m_p->" + func.name + "(" + ps + ");\n"
            s += RmiFile.TABLE2 + "back.clear();\n"
        s += RmiFile.TABLE2 + "co_return;\n"
        s += RmiFile.TABLE + "}\n\n"
        return s        
    pass

    def _to_handle_func_case(module):
        """
        格式化出类似如下的
        void __handle_remote_call__(const RemoteCallMessage& call) override
        {
            switch (call.call_func()) {
                case RPC_FUNC_ID_rc_test:
                    rc_test_call(call);
                    break;
                default:
                    break;
            }
        }
        """
        s = "void __handle_remote_call__(const RemoteCallMessage& call) override\n"
        s += RmiFile.TABLE + "{\n"
        s += RmiFile.TABLE2 + "switch (call.call_func()) {\n"
        for func in module.rmi_func:
            s += RmiFile.TABLE3 + "case " + RmiFile._to_func_id(module.name, func.name) + ':\n'
            s += RmiFile.TABLE4 + func.name + "_handle(call);\n"
            s += RmiFile.TABLE4 + "break;\n"
        s += RmiFile.TABLE4 + "default:" + " break;\n"
        s += RmiFile.TABLE2 + "}\n";
        s += RmiFile.TABLE + "}\n\n"
        return s
    pass

    def _to_handle_co_func_case(module):
        """
        格式化出类似如下的
        cppcoro::task<> __handle_remote_co_call__(const RemoteCallMessage& call, std::string& back) override
        {
            switch (call.call_func()) {
                case RPC_FUNC_ID_rc_test:
                    co_await rc_test_co_call(call, back);
                    break;
                default:
                    break;
            }
            co_return;
        }
        """
        s = "cppcoro::task<> __handle_remote_co_call__(const RemoteCallMessage& call, std::string& back) override\n"
        s += RmiFile.TABLE + "{\n"
        s += RmiFile.TABLE2 + "switch (call.call_func()) {\n"
        for func in module.rmi_func:
            s += RmiFile.TABLE3 + "case " + RmiFile._to_func_id(module.name, func.name) + ':\n'
            s += RmiFile.TABLE4 + "co_await " + func.name + "_co_handle(call, back);\n"
            s += RmiFile.TABLE4 + "break;\n"
        s += RmiFile.TABLE4 + "default:" + " break;\n"
        s += RmiFile.TABLE2 + "}\n";
        s += RmiFile.TABLE2 + "co_return;\n"
        s += RmiFile.TABLE + "}\n\n"
        return s
    pass

    @staticmethod
    def _to_wrapper_func(module, func):
        s = RmiFile.TABLE + "void " + func.name + "(";
        for i in range(len(func.params)):
            if i != 0:
                s += ", "
            s += func.params[i].content
        s += ")\n"
        s += RmiFile.TABLE + "{\n"
        s += RmiFile.TABLE2  + "RemoteCallMessage call_wrapper;\n"
        s += RmiFile.TABLE2  + "call_wrapper.set_allocated_target(new protobuf_service_token(m_token));\n"
        s += RmiFile.TABLE2  + "call_wrapper.set_type(RPC_MSG_TYPE_REMOTE_CALL);\n"
        s += RmiFile.TABLE2  + "call_wrapper.set_call_manager(m_p->hash_code());\n"
        s += RmiFile.TABLE2  + "call_wrapper.set_call_func(" + RmiFile._to_func_id(module.name, func.name)+");\n"
        for p in func.params:
            s += RmiFile.TABLE2  + "call_wrapper.add_params(SerializeAsString("+ p.name +"));\n"
        s += RmiFile.TABLE2  + "auto f = m_p->managers()->find(fist::string_hash_code(\"IRpcManager\"));\n"
        s += RmiFile.TABLE2  + "IRpcManager* p = static_cast<IRpcManager*>(f->second);\n"
        s += RmiFile.TABLE2  + "p->RemoteCall(call_wrapper);\n"
        s += RmiFile.TABLE + "}\n\n"
        return s
    pass

    @staticmethod
    def _to_wrapper_co_func(module, func):
        s = RmiFile.TABLE + "cppcoro::task<" + func.ret_type +"> " + func.name + "_co(";
        for i in range(len(func.params)):
            if i != 0:
                s += ", "
            s += func.params[i].content
        s += ")\n"
        s += RmiFile.TABLE + "{\n"
        s += RmiFile.TABLE2 + "RemoteCallMessage call_wrapper;\n"
        s += RmiFile.TABLE2  + "call_wrapper.set_allocated_target(new protobuf_service_token(m_token));\n"
        s += RmiFile.TABLE2  + "call_wrapper.set_type(RPC_MSG_TYPE_REMOTE_CALL);\n"
        s += RmiFile.TABLE2  + "call_wrapper.set_call_manager(m_p->hash_code());\n"
        s += RmiFile.TABLE2  + "call_wrapper.set_call_func(" + RmiFile._to_func_id(module.name, func.name)+");\n"
        for p in func.params:
            s += RmiFile.TABLE2  + "call_wrapper.add_params(SerializeAsString("+ p.name +"));\n"
        s += RmiFile.TABLE2  + "auto f = m_p->managers()->find(fist::string_hash_code(\"IRpcManager\"));\n"
        s += RmiFile.TABLE2  + "IRpcManager* p = static_cast<IRpcManager*>(f->second);\n"
        s += RmiFile.TABLE2  + "std::string co_ret;\n"
        s += RmiFile.TABLE2  + "co_await p->RemoteCoCall(call_wrapper, co_ret);\n"
        if func.ret_type != "void":
            s += RmiFile.TABLE2  + "co_return ParseFromString<"+ func.ret_type +">(co_ret);\n"
        s += RmiFile.TABLE + "}\n\n"
        return s
    pass

    @staticmethod
    def _to_module_str(mod):
        s_rmi_call = ""
        s_rmi_handle = ""
        for fun in mod.rmi_func:
            s_rmi_call += RmiFile._to_wrapper_func(module=mod, func=fun)
            s_rmi_call += RmiFile._to_wrapper_co_func(module=mod, func=fun)
            s_rmi_handle += RmiFile._to_single_handle_func(module=mod, func=fun)
            s_rmi_handle += RmiFile._to_single_handle_co_func(module=mod, func=fun)
        s_rmi_handle_case = RmiFile._to_handle_func_case(module=mod)
        s_rmi_co_handle_case = RmiFile._to_handle_co_func_case(module=mod)
        s_rmi_co_handle_case = RmiFile._to_handle_co_func_case(module=mod)
        s_rmi_func_id = RmiFile._to_function_enum(module=mod)
        s = RmiFile.TEMPLATE_RMI_STRUCT.substitute(mod_name=mod.module_full_name(), rmi_call=s_rmi_call, 
        rmi_func_id=s_rmi_func_id, rmi_handle=s_rmi_handle, rmi_handle_case=s_rmi_handle_case,
        rmi_co_handle_case=s_rmi_co_handle_case)
        return s

    pass

    def _file_full_path(self):
        file_name = os.path.splitext(self.origin_file)[0]
        project_name = re.findall(r'(.+?)_interface', file_name)
        if len(project_name) != 1:
            print("file incorrect!")
            return None
        # print(project_name)
        return project_name[0] + "_rmi.h"

    def _content(self):
        s = RmiFile.LISENCE
        s += "#include \"" + os.path.basename(self.origin_file) + "\"" \
            "\n#include <core/rpc/rpc_interface.h>\n#include <core/rpc/rpc_helper.h>\n"
        body = ""
        for module in self.modules:
            body += RmiFile._to_module_str(module)
        if len(body) == 0:
            return ""
        return s + body

    def generate(self):
        file_path = self._file_full_path()
        if file_path is None:
            return False
        print(file_path)
        s = self._content()
        if len(s) == 0:
            return True
        with open(file_path, 'wt', encoding='utf-8') as f:
            print(s)
            f.write(self._content())
        return True


def annotation_parser_rmi(filepath):
    file_parser = FileParser(filepath)
    file_parser.parser()
    rmi_file = RmiFile(filepath, file_parser.rmi_modules())
    rmi_file.generate()
    return 0
